﻿using MyCompany.MyProject.Common.LogHelper;
using Serilog;
using Serilog.Events;
using Serilog.Filters;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyCompany.MyProject.Serilog.Extensions;

public static class LoggerConfigurationExtensions
{
    /// <summary>
    /// 记录控制台日志
    /// </summary>
    /// <param name="configuration"></param>
    /// <returns></returns>
    public static LoggerConfiguration WriteToConsole(this LoggerConfiguration configuration)
    {
        // 输出普通日志
        configuration = configuration.WriteTo.Logger(lg => lg.FilterRemoveSqlLog().WriteTo.Console());

        // 输出 SQL 日志
        configuration = configuration.WriteTo.Logger(lg => lg.FilterSqlLog(LogContextStatic.SqlOutToConsole).WriteTo.Console());

        return configuration;
    }

    /// <summary>
    /// 过滤并移除掉SQL日志
    /// </summary>
    /// <param name="lc"></param>
    /// <returns></returns>
    public static LoggerConfiguration FilterRemoveSqlLog(this LoggerConfiguration lc)
    {
        return lc.Filter.ByIncludingOnly(WithProperty<string>(LogContextStatic.LogSource, s => !LogContextStatic.AopSql.Equals(s)));
    }
    /// <summary>
    /// 这个可以不写 使用内置的 Matching.WithProperty<T> 也可以
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="propName"></param>
    /// <param name="predicate"></param>
    /// <returns></returns>
    public static Func<LogEvent, bool> WithProperty<T>(string propName, Func<T, bool> predicate)
    {
        // 如果不包含属性，也就是 true 
        return e =>
        {
            if (!e.Properties.TryGetValue(propName, out var propValue)) return true;
            return propValue is ScalarValue { Value: T value } && predicate(value);
        };
    }

    /// <summary>
    /// 过滤并只保留 SQL 日志
    /// </summary>
    /// <param name="lc"></param>
    /// <returns></returns>
    public static LoggerConfiguration FilterSqlLog(this LoggerConfiguration lc, string propName)
    {
        return lc.Filter.ByIncludingOnly(Matching.WithProperty<string>(LogContextStatic.LogSource, s => LogContextStatic.AopSql.Equals(s)));

       // lc = lc.Filter.ByIncludingOnly(Matching.WithProperty<bool>(propName, s => s));

        // return lc;
    }
    public static IEnumerable<LogEvent> FilterSqlLog(this IEnumerable<LogEvent> batch)
    {
        //只记录 Insert、Update、Delete语句
        return batch.Where(s => s.WithProperty<string>(LogContextStatic.LogSource, q => LogContextStatic.AopSql.Equals(q)))
            .Where(s => s.WithProperty<SugarActionType>(LogContextStatic.SugarActionType,
                q => !new[] { SugarActionType.UnKnown, SugarActionType.Query }.Contains(q)));
    }
    public static bool WithProperty<T>(this LogEvent e, string key, Func<T, bool> predicate)
    {
        if (!e.Properties.TryGetValue(key, out var propertyValue)) return false;

        return propertyValue is ScalarValue { Value: T value } && predicate(value);
    }

    /// <summary>
    /// 记录文件日志
    /// </summary>
    /// <param name="lc"></param>
    /// <returns></returns>
    public static LoggerConfiguration WriteToFile(this LoggerConfiguration lc)
    {
        // 输出 SQL
        lc = lc.WriteTo.Logger(
            lg => lg.FilterSqlLog(LogContextStatic.SqlOutToFile)
                            .WriteToFileConfig(() => LogContextStatic.Combine(LogContextStatic.AopSql, "AopSql.txt"))
            );

        //输出普通日志
        lc = lc.WriteTo.Logger(
            lg => lg.FilterRemoveSqlLog()
                            .WriteToFileConfig(() => LogContextStatic.Combine(LogContextStatic.BasePathLogs, @"Log.txt"))
            );
        return lc;
        ////输出普通日志
        //lc = lc.WriteTo.Logger(lg =>lg
        //    .Filter.ByIncludingOnly(WithProperty<string>(LogContextExtension.LogSource, s => !LogContextExtension.AopSql.Equals(s)))
        //    .WriteTo.Async(s => s.File(Path.Combine("Logs", @"Log.txt"), rollingInterval: RollingInterval.Day,
        //        outputTemplate: LogContextExtension.FileMessageTemplate, retainedFileCountLimit: 31)));
        ////输出SQL
        //lc = lc.WriteTo.Logger(lg =>
        //    lg
        //    .Filter.ByIncludingOnly(Matching.WithProperty<string>(LogContextExtension.LogSource, s => LogContextExtension.AopSql.Equals(s)))
        //    .WriteTo.Async(s => s.File(Path.Combine("Logs", LogContextExtension.AopSql, @"AopSql.txt"), rollingInterval: RollingInterval.Day,
        //            outputTemplate: LogContextExtension.FileMessageTemplate, retainedFileCountLimit: 31)));
        //return lc;
    }
    /// <summary>
    /// 写文件日志的相关配置
    /// </summary>
    /// <param name="lc"></param>
    /// <param name="func"></param>
    /// <returns></returns>
    public static LoggerConfiguration WriteToFileConfig(this LoggerConfiguration lc, Func<string> func)
    {
        return lc.WriteTo.Async(s => s.File(func(), rollingInterval: RollingInterval.Day, outputTemplate: LogContextStatic.FileMessageTemplate, retainedFileCountLimit: 31));
    }

    public static IEnumerable<LogEvent> FilterRemoveOtherLog(this IEnumerable<LogEvent> logs)
    {
        return logs.Where(s => WithProperty<string>(LogContextStatic.LogSource, q => !LogContextStatic.AopSql.Equals(q))(s));
    }
}
